When writing code during the development of software applications, developers commonly spend a significant amount of time “debugging” the code to find runtime and other source code errors. In doing so, developers may take several approaches to reproduce and localize a source code bug, such as observing the behavior of a program based on different inputs, inserting debugging code (e.g., to print variable values, to track branches of execution, etc.), temporarily removing code portions, etc. Tracking down runtime errors to pinpoint code bugs can occupy a significant portion of application development time.
Many types of debugging applications (“debuggers”) have been developed in order to assist developers with the code debugging process. These tools offer developers the ability to trace, visualize, and alter the execution of computer code. For example, debuggers may visualize the execution of code instructions, may present code variable values at various times during code execution, may enable developers to alter code execution paths, and/or may enable developers to set “breakpoints” and/or “watchpoints” on code elements of interest (which, when reached during execution, causes execution of the code to be suspended), among other things.
An emerging form of debugging applications enable “time travel,” “reverse,” or “historic” debugging. With “time travel” debugging, execution of a program (e.g., executable entities such as threads) is recorded/traced by a trace application into one or more trace data streams. These trace data stream(s) can then be used to replay execution of the program later, for both forward and backward analysis. For example, “time travel” debuggers can enable a developer to set forward breakpoints/watchpoints (like conventional debuggers) as well as reverse breakpoints/watchpoints.
Because time travel debuggers record a bit-accurate trace of execution of a program—including both the code that executed and memory values that were read during that program's execution—they have the potential to capture and reveal sensitive code and/or data that, in many cases, should not be made available to those who have access to the resulting trace data (e.g., developers utilizing a debugger that consumes the trace data stream(s)). This may due to security context (e.g., kernel vs. user mode), variations in code authorship (e.g., code developed by one author vs. a called library developed by another author), organizational divisions, policy/legal concerns, etc. For example, time travel debuggers could capture the cryptographic information such as values of encryption keys, random numbers, salts, hashes, nonces, etc.; personally-identifiable information (PII) such as names, mailing addresses, birthdays, social security numbers, e-mail addresses, IP addresses, MAC addresses, etc.; financial information such as credit card numbers, account numbers, financial institutions; authentication information such as usernames, passwords, biometric data, etc.; general inputs such as search terms, file names, etc.; code that may be desired to be kept private; and so on. The ability of time travel debuggers to reveal sensitive information is becoming of increased concern, since time travel debugging technology is advancing to the point that it can have low enough recording overheads to enable it to be employed in production systems—and potentially even in “always on” configurations.